/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *                  The shell acts as a task running in user mode. 
 *       The main function is to make system calls through the user's output.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, 
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include "screen.h"
#include "stdio.h"
#include "syscall.h"
#include "test.h"

void loop_task()
{

    while (1)
    {
        ;
    }
}

#define P5_TEST
struct task_info task0 = {"loop", (uint64_t)&loop_task, USER_PROCESS, 1};

#ifdef P3_TEST

struct task_info task1 = {"task1", (uint64_t)&ready_to_exit_task, USER_PROCESS, 1};
struct task_info task2 = {"task2", (uint64_t)&wait_lock_task, USER_PROCESS, 1};
struct task_info task3 = {"task3", (uint64_t)&wait_exit_task, USER_PROCESS, 1};

struct task_info task4 = {"task4", (uint64_t)&semaphore_add_task1, USER_PROCESS, 1};
struct task_info task5 = {"task5", (uint64_t)&semaphore_add_task2, USER_PROCESS, 1};
struct task_info task6 = {"task6", (uint64_t)&semaphore_add_task3, USER_PROCESS, 1};

struct task_info task7 = {"task7", (uint64_t)&producer_task, USER_PROCESS, 1};
struct task_info task8 = {"task8", (uint64_t)&consumer_task1, USER_PROCESS, 1};
struct task_info task9 = {"task9", (uint64_t)&consumer_task2, USER_PROCESS, 1};

struct task_info task10 = {"task10", (uint64_t)&barrier_task1, USER_PROCESS, 1};
struct task_info task11 = {"task11", (uint64_t)&barrier_task2, USER_PROCESS, 1};
struct task_info task12 = {"task12", (uint64_t)&barrier_task3, USER_PROCESS, 1};

struct task_info task13 = {"SunQuan", (uint64_t)&SunQuan, USER_PROCESS, 1};
struct task_info task14 = {"LiuBei", (uint64_t)&LiuBei, USER_PROCESS, 1};
struct task_info task15 = {"CaoCao", (uint64_t)&CaoCao, USER_PROCESS, 1};

struct task_info task16 = {"multcore", (uint64_t)&test_multicore, USER_PROCESS, 1};
struct task_info task17 = {"affinity", (uint64_t)&test_affinity, USER_PROCESS, 1};
#endif

#ifdef P4_TEST
struct task_info task16 = {"mem_test1", (uint64_t)&rw_task1, USER_PROCESS, 1};
struct task_info task17 = {"plan", (uint64_t)&drawing_task1, USER_PROCESS, 1};
struct task_info task18 = {"memory", (uint64_t)&extend_memory, USER_PROCESS, 1};
struct task_info task19 = {"shm_test0", (uint64_t)&shm_task0, USER_PROCESS, 1};
struct task_info task20 = {"shm_test1", (uint64_t)&shm_task1, USER_PROCESS, 1};
#endif

#ifdef P5_TEST
struct task_info task18 = {"mac_send", (uint64_t)&send_task, USER_PROCESS, 1};
struct task_info task19 = {"mac_recv", (uint64_t)&recv_task, USER_PROCESS, 1};
struct task_info task20 = {"mac_int_recv", (uint64_t)&recv_int_task, USER_PROCESS, 1};
#endif

#ifdef P6_TEST

struct task_info task19 = {"fs_test", (uint64_t)&test_fs, USER_PROCESS, 1};
#endif

static struct task_info *test_tasks[3] = {
    &task18,
    &task19,
    &task20};

#define INPUT_BUFFER_MAX_LENGTH 1000
#define MAX_COMMAND_NUM 5

static char Buffer[INPUT_BUFFER_MAX_LENGTH];
static char *Command[MAX_COMMAND_NUM] = {"ps", "clear", "exec", "kill"};

typedef struct Input_Buffer
{
    char *buff;
    uint64_t buff_length;
    uint64_t input_length;
    int pointer;
} Input_Buffer_t;

static Input_Buffer_t Input_Buffer;
process_show_t ProcessShow[PROCESS_SHOW_MAX_NUM];

static void close_interrupt()
{
    uint32_t status = get_cp0_status();
    status &= 0xfffffffe;
    set_cp0_status(status);
}

static void start_interrupt()
{
    uint32_t status = get_cp0_status();
    status |= 0x01;
    set_cp0_status(status);
}

static void Init_InputBuffer(Input_Buffer_t *p)
{
    p->buff = Buffer;
    p->buff_length = INPUT_BUFFER_MAX_LENGTH;
    p->input_length = 0;
    p->pointer = 0;
}

static void Init_ProcessShow()
{
    int i;
    for (i = 0; i < 20; i++)
    {
        ProcessShow[i].num = 0;
        ProcessShow[i].status = TASK_EXITED;
    }
}

char get_input()
{
    char ch = 0;
    unsigned char *data = (unsigned char *)0xffffffffbfe00000;
    unsigned char *status = (unsigned char *)0xffffffffbfe00005;

    while ((*status) & 0x01)
    {
        ch = *data;
    }
    return ch;
}

#define ENUM_TYPE_CASE(x) \
    case x:               \
        return (#x);

static inline const char *trans_status(task_status_t status)
{
    switch (status)
    {
        ENUM_TYPE_CASE(TASK_BLOCKED)
        ENUM_TYPE_CASE(TASK_RUNNING)
        ENUM_TYPE_CASE(TASK_READY)
        ENUM_TYPE_CASE(TASK_EXITED)
        ENUM_TYPE_CASE(TASK_SLEEPING)
    }
    return "Invalid Status";
}

void test_shell()
{
    sys_move_cursor(1, 50);
    printf("\r--------------------- COMMAND ---------------------\n");
    printf("> root@UCAS_OS: ");

    Input_Buffer_t *input_buffer_p = &Input_Buffer;
    Init_ProcessShow();
    Init_InputBuffer(input_buffer_p);

    int i = 0;

    while (1)
    {
        close_interrupt();
        char ch = get_input();
        start_interrupt();

        if (ch != 0)
        {
            printf("%c", ch);
            *(input_buffer_p->buff + i) = ch;
            input_buffer_p->input_length++;
            i++;
            if (ch == '\b')
            {
                i -= 2;
                printf(" \b");
            }
            else if (ch == '\r')
            {
                if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'p' &&
                    *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 's')
                {
                    printf("[PROCESS TABLE]\n");
                    sys_ps();
                    int j = 0;
                    while (ProcessShow[j].num >= 0)
                    {
                        printf("[%d] PID: %d ", ProcessShow[j].num, ProcessShow[j].pid);
                        printf("STATUS: %s CORE: %d\n", trans_status(ProcessShow[j].status), ProcessShow[j].core);
                        j++;
                    }
                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'l' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 3) == 'a' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 4) == 'r')
                {
                    sys_screen_clear();
                    sys_move_cursor(1, 50);
                    printf("\r--------------------- COMMAND ---------------------\n");
                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'x' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 3) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 4) == ' ')
                {
                    if (*(input_buffer_p->buff + input_buffer_p->pointer + 5) - '0' >= 0 &&
                        *(input_buffer_p->buff + input_buffer_p->pointer + 5) - '0' <= 9)
                    {
                        int j = 5, k1 = 0, k2 = 0;
                        int base = 1;
                        int note = 0;
                        while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != '\r' && *(input_buffer_p->buff + input_buffer_p->pointer + j) != ' ')
                        {
                            k1 = k1 * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                            base *= 10;
                            j++;
                        }
                        printf("exec test_task[%d].\n", k1);
                        base = 1;
                        while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != '\r')
                        {
                            note = 1;
                            if (*(input_buffer_p->buff + input_buffer_p->pointer + j) != ' ')
                            {
                                k2 = k2 * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                                base *= 10;
                            }
                            j++;
                        }
                        sys_spawn(test_tasks[k1], IGNORE, IGNORE);
                        if (note == 1)
                        {
                            printf("exec test_task[%d].\n", k2);
                            sys_spawn(test_tasks[k2], IGNORE, IGNORE);
                        }
                    }
                    else if (*(input_buffer_p->buff + input_buffer_p->pointer + 5) == '-')
                    {
                        if (*(input_buffer_p->buff + input_buffer_p->pointer + 6) == 'a')
                        {
                            int num = *(input_buffer_p->buff + input_buffer_p->pointer + 8) - '0';
                            int i, j = 9;
                            uint64_t arg[3];
                            for (i = 0; i < 3; i++)
                                arg[i] = 0; // init
                            for (i = 0; i < num; i++)
                            {
                                j += 3;
                                uint64_t k = 0;
                                int base = 1;
                                while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != ' ')
                                {
                                    k = k * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                                    base = 16;
                                    j++;
                                }
                                arg[i] = k;
                            }
                            j++; // a space
                            int k1 = 0;
                            int base = 1;
                            while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != '\r')
                            {
                                k1 = k1 * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                                base *= 10;
                                j++;
                            }
                            sys_spawn(test_tasks[k1], arg[0], arg[1]);
                            printf("exec test_task[%d].\n", k1);
                        }
                        else
                        {
                            printf("> input error!!\n");
                        }
                    }
                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'k' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'i' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 'l' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 3) == 'l' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 4) == ' ' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 5) - '0' >= 0)
                {
                    int j = 5, k = 0;
                    int base = 1;
                    while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != '\r')
                    {
                        k = k * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                        base *= 10;
                        j++;
                    }
                    printf("kill process PID: %d.\n", k);

                    sys_kill(k);

                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'p' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 'b')
                {
                    int i = 0;
                    for (i = 0; i < NUM_MAX_TASK; i++)
                    {
                        if (pcb[i].status != TASK_EXITED)
                            printf("pcb[%d] - STATUS: %s - pid: %d\n", i, trans_status(pcb[i].status), pcb[i].pid);
                    }
                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 't' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'a' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 's' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 3) == 'k' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 4) == 's' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 5) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 6) == 't' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 7) == ' ')
                {
                    if (*(input_buffer_p->buff + input_buffer_p->pointer + 8) == '-')
                    {
                        int j = 11, core = 0;
                        int base = 1;
                        while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != ' ')
                        {
                            core = core * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                            base *= 10;
                            j++;
                        }
                        int pid = 0;
                        base = 1;
                        j++;
                        while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != '\r')
                        {
                            pid = pid * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                            base *= 10;
                            j++;
                        }
                        printf("pid=%d, core=%d\n", pid, core - 1);
                        sys_taskset_pid(pid, core - 1);
                    }
                    else
                    {
                        int j = 8, core = 0;
                        int base = 1;
                        while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != ' ')
                        {
                            core = core * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                            base *= 10;
                            j++;
                        }
                        int task_id = 0;
                        j++;
                        base = 1;
                        while (*(input_buffer_p->buff + input_buffer_p->pointer + j) != '\r')
                        {
                            task_id = task_id * base + (*(input_buffer_p->buff + input_buffer_p->pointer + j) - '0');
                            base *= 10;
                            j++;
                        }
                        sys_taskset0(test_tasks[task_id], core - 1);
                        printf("taskset task%d, cpu: %d\n", task_id, core - 1);
                    }
                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'h' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 3) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 4) == 'k' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 5) == ' ' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 6) == 'f' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 7) == 'r' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 8) == 'a' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 9) == 'm' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 10) == 'e')
                {
                    check_page_frame();
                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'h' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 3) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 4) == 'k' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 5) == ' ' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 6) == 'f' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 7) == 'r' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 8) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 9) == 'e')
                {
                    check_free_list();
                    printf("> root@UCAS_OS: ");
                }
                else if (*(input_buffer_p->buff + input_buffer_p->pointer) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 1) == 'h' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 2) == 'e' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 3) == 'c' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 4) == 'k' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 5) == ' ' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 6) == 'b' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 7) == 'u' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 8) == 's' &&
                         *(input_buffer_p->buff + input_buffer_p->pointer + 9) == 'y')
                {
                    check_busy_list();
                    printf("> root@UCAS_OS: ");
                }
                else
                {
                    printf("Error input!!!\n");
                    printf("> root@UCAS_OS: ");
                }
                input_buffer_p->pointer = i;
            }
        }
    }
}